﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Pavilion;

namespace Pavilion.GameTools
{
    public class EnvironmentTool : GameToolBase
    {
        public string ResourceName { get { return resourceName; } set { resourceName = value; if (resourceName != "") LoadEnvironment(resourceName); } }

        static private Dictionary<string, Action<EnvironmentTool, object>> dataExecutionDictionary;
        private List<ImageLayer> backgroundLayersList;
        private DrawVisibilityGroup drawVisibilityGroup;
        private List<PolygonPhysicalData> polygonsOnGround;
        private MultiLinePhysicalData multiLineData;
        private DrawItem drawItem;
        private string resourceName;

        public EnvironmentTool(GameItem parentGameItem) : base(parentGameItem)
        {
            Initialize();
        }

        private void Initialize()
        {
            backgroundLayersList = new List<ImageLayer>();
            multiLineData = new MultiLinePhysicalData(this);
            polygonsOnGround = new List<PolygonPhysicalData>();

            LoadDataExecution();
        }

        public override void Dispose()
        {
            foreach (ImageLayer imageLayer in backgroundLayersList)
                imageLayer.Dispose();

            backgroundLayersList.Clear();

            CollisionManager.Instance.RemoveMultiLineData(multiLineData);
            drawItem.Dispose();

            base.Dispose();
        }

        private void LoadDataExecution()
        {
            if (dataExecutionDictionary != null)
                return;

            dataExecutionDictionary = new Dictionary<string, Action<EnvironmentTool, object>>();
            dataExecutionDictionary.Add("ResourceName", (environmentTool, value) => environmentTool.ResourceName = (string)value);
        }

        public override void UpdateProperty(string propertyName, object propertyValue)
        {
            dataExecutionDictionary[propertyName].Invoke(this, propertyValue);
        }

        public override void Load()
        {
            drawVisibilityGroup = ParentGameItem.DrawVisibilityGroup;

            CollisionManager.Instance.AddMultiLineData(multiLineData);
            CollisionManager.Instance.RegisterToMultiLineData(multiLineData, (p) =>
            {
                foreach (PolygonPhysicalData polygonData in p.NewIntersectedPolygons)
                    AddPolygonToGround(polygonData);

                foreach (PolygonPhysicalData polygonData in p.RemovedIntersectedPolygons)
                    RemovePolygonToGround(polygonData);
            });

            drawItem = new DrawItem(50, () =>
            {
                foreach (List<Vector2> lineSequence in multiLineData.LineSequenceCollection)
                {
                    for (int i = 1; i < lineSequence.Count; i++)
                        DrawManager.Instance.DrawLine(lineSequence[i - 1], lineSequence[i], Color.Black);
                }
            });
            DrawManager.Instance.Add(drawItem);
            drawVisibilityGroup.Add(drawItem);
        }

        private void LoadEnvironment(string resourceName)
        {
            string directoryPath = GameEngine.GameResourcesDirectory + @"Environments\" + resourceName;

            XElement scriptNode = XDocument.Load(directoryPath + @"\script.xml").Element("script");
            IEnumerable<XElement> xmlLayerList = scriptNode.Element("LayerList").Elements("Layer");

            foreach (XElement layerNode in xmlLayerList)
                CreateImageLayer(layerNode, directoryPath + @"\Built Data");

            XElement environmentLineListNode = scriptNode.Element("EnvironmentLineList");

            if (environmentLineListNode != null)
            {
                IEnumerable<XElement> xmlLineList = environmentLineListNode.Elements("Line");

                foreach (XElement lineNode in xmlLineList)
                    CreateLineSequence(lineNode.Attribute("Sequence").Value);
            }
        }

        private void AddPolygonToGround(PolygonPhysicalData polygonData)
        {
            //polygonsOnGround.Add(polygonData);
            //polygonData.PositionChanged += new PositionChangedHandler(PolygonData_PositionChanged);

            //foreach (List<Vector2> lineSequence in multiLineData.LineSequenceCollection)
            //{
            //    for (int i = 1; i < lineSequence.Count; i++)
            //    {
            //        LineVector lineVector = new LineVector(lineSequence[i - 1], lineSequence[i] - lineSequence[i - 1]);
            //        if (LineVector.Intersects(polygonData.PhysicalBounds, lineVector))
            //            polygonData.SetPosition(polygonData.Position.X, lineVector.DetermineYPointOfLine(polygonData.Position.X) - 5);
            //    }
            //}
        }

        private void RemovePolygonToGround(PolygonPhysicalData polygonData)
        {
            //if (polygonsOnGround.Remove(polygonData))
            //    polygonData.PositionChanged -= new PositionChangedHandler(PolygonData_PositionChanged);
        }

        private bool isChanging;
        private void PolygonData_PositionChanged(PhysicalData physicalData, float xDistance, float yDistance)
        {
            if (isChanging)
                return;

            isChanging = true;
            PolygonPhysicalData polygonData = physicalData as PolygonPhysicalData;
            foreach (List<Vector2> lineSequence in multiLineData.LineSequenceCollection)
            {
                for (int i = 1; i < lineSequence.Count; i++)
                {
                    LineVector lineVector = new LineVector(lineSequence[i - 1], lineSequence[i] - lineSequence[i - 1]);
                    if (LineVector.Intersects(polygonData.PhysicalBounds, lineVector))
                        polygonData.SetPosition(polygonData.Position.X, lineVector.DetermineYPointOfLine(polygonData.Position.X));
                }
            }

            isChanging = false;
        }

        private void CreateLineSequence(string lineSequenceValue)
        {
            List<Vector2> lineSequence = new List<Vector2>();

            foreach (string point in lineSequenceValue.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
            {
                string[] pointValues = point.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                lineSequence.Add(new Vector2(float.Parse(pointValues[0]), float.Parse(pointValues[1])));
            }

            multiLineData.LineSequenceCollection.Add(lineSequence);
        }

        private void CreateImageLayer(XElement layerNode, string directoryPath)
        {
            ImageLayer imageLayer = new ImageLayer(drawVisibilityGroup);
            imageLayer.Deserialize(layerNode);
            imageLayer.Load(directoryPath);

            backgroundLayersList.Add(imageLayer);
        }
    }
}
